package com.dimples.dd.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.shaded.com.google.common.annotations.VisibleForTesting;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dimples.dd.common.pojo.PageResult;
import com.dimples.dd.system.convert.Oauth2RegisteredClientConvert;
import com.dimples.dd.system.mapper.Oauth2RegisteredClientMapper;
import com.dimples.dd.system.model.entity.Oauth2RegisteredClientDO;
import com.dimples.dd.system.model.form.OAuth2ClientForm;
import com.dimples.dd.system.model.query.OAuth2ClientPageQuery;
import com.dimples.dd.system.model.vo.OAuth2ClientPageVO;
import com.dimples.dd.system.service.Oauth2RegisteredClientService;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.stereotype.Service;

import java.util.List;

import static com.dimples.dd.common.exception.BizExceptionUtil.exception;
import static com.dimples.dd.common.exception.ErrorCode.OAUTH2_CLIENT_EXISTS;
import static com.dimples.dd.common.exception.ErrorCode.OAUTH2_CLIENT_NOT_EXISTS;

@Service
@RequiredArgsConstructor
public class Oauth2RegisteredClientServiceImpl extends ServiceImpl<Oauth2RegisteredClientMapper, Oauth2RegisteredClientDO> implements Oauth2RegisteredClientService{

    private final PasswordEncoder passwordEncoder;
    private final JdbcTemplate jdbcTemplate;
    private final Oauth2RegisteredClientConvert oauth2RegisteredClientConvert;

    @Override
    public String createOAuth2Client(OAuth2ClientForm oAuth2ClientForm) {
        if (StrUtil.isBlank(oAuth2ClientForm.getId())) {
            // 校验 Client 未被占用
            validateClientIdExists(null, oAuth2ClientForm.getClientId());
        } else {
            // 校验 Client 未被占用
            validateClientIdExists(oAuth2ClientForm.getId(), oAuth2ClientForm.getClientId());
        }

        // 插入
        if (StrUtil.isBlank(oAuth2ClientForm.getId())) {
            oAuth2ClientForm.setId(IdUtil.getSnowflakeNextIdStr());
        }

        RegisteredClient registeredClient = OAuth2ClientForm.toRegisteredClient(oAuth2ClientForm, passwordEncoder);
        JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        jdbcRegisteredClientRepository.save(registeredClient);

        Oauth2RegisteredClientDO client = new Oauth2RegisteredClientDO();
        client.setId(oAuth2ClientForm.getId());
        client.setClientLogo(oAuth2ClientForm.getClientLogo());
        client.setClientDescription(oAuth2ClientForm.getClientDescription());
        client.setStatus(oAuth2ClientForm.getStatus().byteValue());
        this.baseMapper.updateById(client);

        return client.getId();
    }

    @Override
    public void updateOAuth2Client(OAuth2ClientForm updateForm) {
        // 校验存在
        validateOAuth2ClientExists(CollUtil.newArrayList(updateForm.getId()));

        this.createOAuth2Client(updateForm);
    }

    private void validateOAuth2ClientExists(List<String> ids) {
        List<Oauth2RegisteredClientDO> list = this.baseMapper.selectBatchIds(ids);
        if (CollUtil.isEmpty(list)) {
            throw exception(OAUTH2_CLIENT_NOT_EXISTS, StrUtil.join(StrUtil.COMMA, ids));
        }
        List<String> idsByDb = list.stream().map(Oauth2RegisteredClientDO::getId).toList();
        List<String> subtract = CollUtil.subtractToList(ids, idsByDb);
        if (CollUtil.isNotEmpty(subtract)) {
            throw exception(OAUTH2_CLIENT_NOT_EXISTS, StrUtil.join(StrUtil.COMMA, subtract));
        }
    }

    @VisibleForTesting
    void validateClientIdExists(String id, String clientId) {
        Oauth2RegisteredClientDO client = this.baseMapper.selectByClientId(clientId);
        if (client == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的客户端
        if (id == null) {
            throw exception(OAUTH2_CLIENT_EXISTS);
        }
        if (!StrUtil.equals(id, client.getId())) {
            throw exception(OAUTH2_CLIENT_EXISTS);
        }
    }

    @Override
    public void deleteOAuth2Client(String ids) {
        List<String> idList = StrUtil.split(ids, StrUtil.COMMA);

        // 校验存在
        validateOAuth2ClientExists(idList);
        // 删除
        this.baseMapper.deleteBatchIds(idList);
    }

    @Override
    public OAuth2ClientForm selectOAuth2Client(String id) {
        Oauth2RegisteredClientDO client = this.baseMapper.selectById(id);

        JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        RegisteredClient registeredClient = jdbcRegisteredClientRepository.findById(id);

        OAuth2ClientForm oAuth2ClientForm = this.oauth2RegisteredClientConvert.entity2Form(client);
        if (registeredClient != null) {
            oAuth2ClientForm.setAccessTokenTimeToLive(registeredClient.getTokenSettings().getAccessTokenTimeToLive().toSeconds());
            oAuth2ClientForm.setRefreshTokenTimeToLive(registeredClient.getTokenSettings().getRefreshTokenTimeToLive().toSeconds());
        }
        return oAuth2ClientForm;
    }

    @Override
    public PageResult<OAuth2ClientPageVO> selectOAuth2ClientPage(OAuth2ClientPageQuery pageQuery) {
        PageResult<Oauth2RegisteredClientDO> pageResult =  this.baseMapper.selectPage(pageQuery);

        PageResult<OAuth2ClientPageVO> pageResultVo = this.oauth2RegisteredClientConvert.entity2Page(pageResult);
        JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        for (OAuth2ClientPageVO record : pageResultVo.getData().getRecords()) {
            RegisteredClient registeredClient = jdbcRegisteredClientRepository.findById(record.getId());
            if (registeredClient != null) {
                record.setAccessTokenTimeToLive(registeredClient.getTokenSettings().getAccessTokenTimeToLive().toSeconds());
                record.setRefreshTokenTimeToLive(registeredClient.getTokenSettings().getRefreshTokenTimeToLive().toSeconds());
            }
        }

        return pageResultVo;
    }
}
